#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <i86.h>

#include "PCITYPES.H"
#include "pci.h"
//
// Copyright (c) 2004, 2006 Philipp Diethelm
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software, to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to
// do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
//  pci_types.h
//  Version: 1.1
//
//  Version history
//  1.0  23.01.2006  Initial release
//  1.1  30.01.2006  Changed return type of IsPciBiosPresent() to pcibool
//  1.2  30.01.2006  Added PciSearchForDevice()
//  1.3  31.01.2006  Updated code to uint??_t datatypes
//



//
// Private variable - Read through PciGetLastError()
//
static uint8_t pciLastError = 0;

//
//  PciGetLastError()
//
//  Input:  Nothing
//
//  Return: Last error (AH) from a BIOS call
//

uint8_t PciGetLastError()
{
        return pciLastError;
}

//
//  IsPciBiosPresent
//
//  Input:  Nothing
//
//  Return: TRUE if PCI Bios is present
//          FALSE if PCI Bios not present
//
pcibool IsPciBiosPresent()
{
        union REGS regs;

        regs.w.ax = PCI_BIOS_INSTALL;
        regs.x.edi = 0;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;

        if((regs.h.ah==PCI_ERR_SUCCESS) && (regs.x.edx==PCI_SIGNATURE))
                return pci_true;

        return pci_false;
}

//
//  GetPciBiosVerLo()
//
//  Input:  Nothing
//
//  Return: PCI Bios Minor Version
//
uint8_t GetPciBiosVerLo()
{
        union REGS regs;

        regs.w.ax = PCI_BIOS_INSTALL;
        regs.x.edi = 0;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;

        if((regs.h.ah==PCI_ERR_SUCCESS) && (regs.x.edx==PCI_SIGNATURE))
                return regs.h.bl;

        return 0;
}

//
//  GetPciBiosVerHi()
//
//  Input:  Nothing
//
//  Return: PCI Bios Major Version
//
uint8_t GetPciBiosVerHi()
{
        union REGS regs;

        regs.w.ax = PCI_BIOS_INSTALL;
        regs.x.edi = 0;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;

        if((regs.h.ah==PCI_ERR_SUCCESS) && (regs.x.edx==PCI_SIGNATURE))
                return regs.h.bh;

        return 0;
}

//
//  GetPciBussesInSystem()
//
//  Input:  Nothing
//
//  Return: PCI Bus count
//
uint8_t GetPciBussesInSystem()
{
        union REGS regs;

        regs.w.ax = PCI_BIOS_INSTALL;
        regs.x.edi = 0;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;

        if((regs.h.ah==PCI_ERR_SUCCESS) && (regs.x.edx==PCI_SIGNATURE))
                return regs.h.cl;

        return 0;
}

//
//  GetPciDeviceCount()
//
//  Input:  Nothing
//
//  Return: total PCI device count
//
uint16_t GetPciDeviceCount()
{
        union REGS regs;
        uint16_t numDev = 0;
        uint8_t  bus;
        uint8_t  dev;
        uint8_t  func;

        for(bus=0; bus<=GetPciBussesInSystem(); bus++)
        {
                for(dev=0; dev<=PCI_DEV_MAX; dev++)
                {
                        // Check if device present
                        if(0xFFFF!=PciReadConfigWord(bus, dev, 0, PCI_CFG_R16_VENID))
                        {
                                uint8_t ucHeaderType = PciReadConfigByte(bus, dev, 0, PCI_CFG_R8_HDRTYPE);
                                ucHeaderType &= 0x80;

                                for(func=0; func<=PCI_FUNC_MAX; func++)
                                {
                                        if(0xFFFF != PciReadConfigWord(bus, dev, func, 0))
                                        {
                                                if(ucHeaderType==0 && func==0)
                                                        numDev++;
                                                else if(ucHeaderType==0x80)
                                                        numDev++;
                                        }
                                }
                        }
                }
        }

        return numDev;
}

//
//  PciReadConfigByte()
//
//  Input:  bus  - PCI bus number
//          dev  - PCI device number
//          func - PCI function number
//          reg  - PCI configuration register
//
//  Return: configuration register data
//
uint8_t PciReadConfigByte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg)
{
        union REGS regs;
        uint8_t devfunc = 0;

        // Mask the input values
        dev  &= PCI_DEV_MAX;
        func &= PCI_FUNC_MAX;

        // Make the conbined dev/func parameter
        devfunc = dev;
        devfunc <<= 3;
        devfunc |= func;

        // Call the BIOS
        regs.w.ax = PCI_BIOS_CFG_RD8;
        regs.h.bh = bus;
        regs.h.bl = devfunc;
        regs.w.di = reg;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;
        return regs.h.cl;
}

//
//  PciReadConfigWord()
//
//  Input:  bus  - PCI bus number
//          dev  - PCI device number
//          func - PCI function number
//          reg  - PCI configuration register
//
//  Return: configuration register data
//
uint16_t PciReadConfigWord(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg)
{
        union REGS regs;
        uint8_t devfunc = 0;

        // Mask the input values
        dev  &= PCI_DEV_MAX;
        func &= PCI_FUNC_MAX;

        // Make the conbined dev/func parameter
        devfunc = dev;
        devfunc <<= 3;
        devfunc |= func;

        // Call the BIOS
        regs.w.ax = PCI_BIOS_CFG_RD16;
        regs.h.bh = bus;
        regs.h.bl = devfunc;
        regs.w.di = reg;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;
        return regs.w.cx;
}

//
//  PciReadConfigDWord()
//
//  Input:  bus  - PCI bus number
//          dev  - PCI device number
//          func - PCI function number
//          reg  - PCI configuration register
//
//  Return: configuration register data
//
uint32_t PciReadConfigDWord(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg)
{
        union REGS regs;
        uint8_t devfunc = 0;

        // Mask the input values
        dev  &= PCI_DEV_MAX;
        func &= PCI_FUNC_MAX;

        // Make the conbined dev/func parameter
        devfunc = dev;
        devfunc <<= 3;
        devfunc |= func;

        // Call the BIOS
        regs.w.ax = PCI_BIOS_CFG_RD32;
        regs.h.bh = bus;
        regs.h.bl = devfunc;
        regs.w.di = reg;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;
        return regs.x.ecx;
}

//
//  PciWriteConfigByte()
//
//  Input:  bus  - PCI bus number
//          dev  - PCI device number
//          func - PCI function number
//          reg  - PCI configuration register
//          data - Data to write to register
//
//  Return: pciLastError
//
uint8_t PciWriteConfigByte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint8_t data)
{
        union REGS regs;
        uint8_t devfunc = 0;

        // Mask the input values
        dev  &= PCI_DEV_MAX;
        func &= PCI_FUNC_MAX;

        // Make the conbined dev/func parameter
        devfunc = dev;
        devfunc <<= 3;
        devfunc |= func;

        // Call the BIOS
        regs.w.ax = PCI_BIOS_CFG_WR8;
        regs.h.bh = bus;
        regs.h.bl = devfunc;
        regs.h.cl = data;
        regs.w.di = reg;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;
        return pciLastError;
}

//
//  PciWriteConfigWord()
//
//  Input:  bus  - PCI bus number
//          dev  - PCI device number
//          func - PCI function number
//          reg  - PCI configuration register
//          data - Data to write to register
//
//  Return: pciLastError
//
uint8_t PciWriteConfigWord(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint16_t data)
{
        union REGS regs;
        uint8_t devfunc = 0;

        // Mask the input values
        dev  &= PCI_DEV_MAX;
        func &= PCI_FUNC_MAX;

        // Make the conbined dev/func parameter
        devfunc = dev;
        devfunc <<= 3;
        devfunc |= func;

        // Call the BIOS
        regs.w.ax = PCI_BIOS_CFG_WR16;
        regs.h.bh = bus;
        regs.h.bl = devfunc;
        regs.w.cx = data;
        regs.w.di = reg;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;
        return pciLastError;
}

//
//  PciWriteConfigDWord()
//
//  Input:  bus  - PCI bus number
//          dev  - PCI device number
//          func - PCI function number
//          reg  - PCI configuration register
//          data - Data to write to register
//
//  Return: pciLastError
//
uint8_t PciWriteConfigDWord(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, uint32_t data)
{
        union REGS regs;
        uint8_t devfunc = 0;

        // Mask the input values
        dev  &= PCI_DEV_MAX;
        func &= PCI_FUNC_MAX;

        // Make the conbined dev/func parameter
        devfunc = dev;
        devfunc <<= 3;
        devfunc |= func;

        // Call the BIOS
        regs.w.ax = PCI_BIOS_CFG_WR32;
        regs.h.bh = bus;
        regs.h.bl = devfunc;
        regs.x.ecx = data;
        regs.w.di = reg;
        int386(PCI_INT, &regs, &regs);

        pciLastError = regs.h.ah;
        return pciLastError;
}

//
//  PciSearchForDevice()
//
//  Input:  venid  - PCI vendor ID
//          devid  - PCI device ID
//          marker - Set to 0 on first call. will be incremented for every device found!
//
//  Return: pci_true if there are more devices to search for
//          pci_false if no more devices are present
//
//          bus    - bus of device found
//          dev    - dev of device found
//          func   - function of device found
//          marker - marker+1
//
pcibool PciSearchForDevice(uint16_t venid, uint16_t devid, uint8_t* bus, uint8_t* dev, uint8_t* func, uint16_t* marker)
{
        union REGS regs;

        // Check parameters
        if(0==bus || 0==dev || 0==func || 0==marker)
        {
                return pci_false;
        }

        // Call the BIOS	
        regs.w.ax = PCI_BIOS_FINDDEV;
        regs.w.dx = venid;
        regs.w.cx = devid;
        regs.w.si = *marker;
        int386(PCI_INT, &regs, &regs);

        // Device found?
        if(regs.h.ah==PCI_ERR_SUCCESS)
        {
                // fill in the return values
                *bus = regs.h.bh;
                *dev = regs.h.bl>>3;
                *func = regs.h.bl&0x7;

                // search for next device
                *marker += 1;

                return pci_true;
        }

        return pci_false;
}
